﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net.Sockets;
using TekaMUD.Engine.Blocks;
using System.Runtime.Remoting.Messaging;
using TekaMUD.Engine.Commands;
using TekaMUD.Engine;
using TekaMUD.Engine.Framework;


namespace TekaMUD.Engine.IO
{
	[Obsolete("Needs locking. Accesed by IO threads and command queue. More than 1 lock (socket, string builder)")]
	public class TcpNetworkChannel : BaseChannel
	{
		#region Attributes
		private ChannelStatusEnum _status;
		private object SyncRoot = new object();
		private Socket _socket;
		private Encoding ChannelEncoding = Encoding.Default;
		private List<byte[]> outputBufferList;
		private StringBuilder _inputStringBuilder;
		private uint inputPtr;
		private byte[] inputBuffer;
		private bool _outputDelayed;
		#endregion

		[Obsolete("Investigate using TcpServer classes instead of a socket.")]
		public TcpNetworkChannel(Socket s, Player p)
		{
			_socket = s;

			outputBufferList = new List<byte[]>();
			_inputStringBuilder = new StringBuilder((int) Configuration.NETWORK_BUFFER_SIZE_IN_BYTES);
			_outputDelayed = false;
			inputBuffer = new byte[Configuration.NETWORK_BUFFER_SIZE_IN_BYTES];
			Player = p;

			// Set options.
			Status = ChannelStatusEnum.Negotiation;

			//Sends the echo command.
			_socket.Send(TelnetNegotiation.TELNET_SHOW_ECHO);
			
			byte[] negBuf = new byte[1000];

			_socket.Receive(negBuf);
			Status = ChannelStatusEnum.Opened;

			WriteString(PlayerMgmtCommands.GetMOTD(), AnsiColors.Magenta, null, false, false);

			// Begin read.
			s.BeginReceive(inputBuffer, 0, (int)Configuration.NETWORK_BUFFER_SIZE_IN_BYTES, 0, new AsyncCallback(TcpNetworkChannel.EndReceiveCallback), this);
		}

		private static void EndReceiveCallback(IAsyncResult iar)
		{
			TcpNetworkChannel tnc = (TcpNetworkChannel)iar.AsyncState;
			Socket s = tnc._socket;
			int bytesRead = s.EndReceive(iar);

			if (bytesRead > 0)
			{
				tnc.AppendInputString((tnc.ChannelEncoding.GetString(tnc.inputBuffer, 0, bytesRead)));
				tnc.ScanAndDispatch();
			}

			// Loop again.
			s.BeginReceive(tnc.inputBuffer, 0, (int)Configuration.NETWORK_BUFFER_SIZE_IN_BYTES, 0, new AsyncCallback(TcpNetworkChannel.EndReceiveCallback), tnc);	
		}

		/// <summary>
		/// Appends a new input string to the string builder buffer
		/// </summary>
		[Obsolete("Check for input string limits!")]
		private void AppendInputString(string s)
		{
			lock (SyncRoot)
			{
				_inputStringBuilder.Append(s);
			}
		}

		/// <summary>
		/// Scans the input string builder for fully formed commands and dispatches them to the command queue.
		/// </summary>
		private void ScanAndDispatch()
		{
			lock (SyncRoot)
			{
				
			restart:
			
				for (int i = 0; i < _inputStringBuilder.Length; i++)
					if (_inputStringBuilder[i] == '\n')
					{
						string command = _inputStringBuilder.ToString(0, i);

						CommandQueue.BuildCommand(this, Player, command);

						_inputStringBuilder.Remove(0, i +1);

						goto restart;
					}
			}
		}

		#region Write methods


		public override void WriteString(string s)
		{
			WriteStringToBuffer(s);
		}

		public override void WriteString(string s, AnsiColors fgColor, AnsiColors? bgColor, bool brightFG, bool brightBG)
		{
			StringBuilder sb = new StringBuilder();

			sb.Append(AnsiUtils.ANSI_CSI);
			sb.Append((brightFG ? 90 : 30) + ((int)fgColor));

			if (bgColor.HasValue)
			{
				sb.Append(";");
				sb.Append((brightBG ? 100 : 40) + ((int)fgColor));
			}

			sb.Append("m");
			sb.Append(s);

			// Reset previous color codes.
			sb.Append(AnsiUtils.ANSI_CSI);
			sb.Append("m");

			WriteStringToBuffer(sb.ToString());
		}

		public override void WriteLine(string s, AnsiColors fgColor, AnsiColors? bgColor, bool brightFG, bool brightBG)
		{
			WriteString(s + "\r\n", fgColor, bgColor, brightFG, brightBG);
		}

		private void WriteStringToBuffer(string s)
		{
			int count = ChannelEncoding.GetByteCount(s);
			byte[] buf = ChannelEncoding.GetBytes(s);

			// Append to the output buffer.
			lock (SyncRoot)
			{
				outputBufferList.Add(buf);
			}

			if (!_outputDelayed)
				Flush();
		}

		/// <summary>
		/// Sends a byte array to the socket, without escaping.
		/// </summary>
		public void SendRawByteArray(byte[] buf)
		{
			int ret;

			lock (SyncRoot)
			{
				ret = _socket.Send(buf);
			}

			if (ret != buf.Length)
			{
				throw new Exception("Unable to send specified number of bytes. Sent: " + ret);
			}
		}


		public void HoldOutput()
		{
			_outputDelayed = true;
		}

		public void ReleaseOutput()
		{
			_outputDelayed = false;
			Flush();
		}

	
		#endregion

		public override void Close()
		{
			throw new NotImplementedException();
		}

		[Obsolete("BROKEN!")]
		public void Flush()
		{
			/* Flush output bytes */

			lock (SyncRoot)
			{
//				SocketError error;

				// Remove the buffers to send from the list
				byte[][] toSendLists = outputBufferList.ToArray();
				outputBufferList.Clear();

				// Send all the buffers so far.
			//	_socket.BeginSend(toSendLists, SocketFlags.None, out error, new AsyncCallback(TcpNetworkChannel.SendCompletedCallback), this);
			}
		}

		[Obsolete("Improve logging. Deal with errors")]
		private static void SendCompletedCallback(IAsyncResult iar)
		{
			TcpNetworkChannel tnc = iar.AsyncState as TcpNetworkChannel;
			SocketError error;

			tnc._socket.EndSend(iar, out error);

			if (error != SocketError.Success)
			{
				Logger.LogError("Socket send error: " + error.ToString());
			}
		}

		public override void SendPrompt()
		{
			throw new NotImplementedException();
		}

		protected override void FlushHolderToStream(ChannelOutputHolder coh)
		{
			throw new NotImplementedException();
		}
	}
}
